package com.leyou.auth.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.leyou.auth.config.JwtProperties;
import com.leyou.auth.mapper.ApplicationInfoMapper;
import com.leyou.auth.pojo.ApplicationInfo;
import com.leyou.client.user.UserClient;
import com.leyou.common.auth.JwtUtils;
import com.leyou.common.auth.pojo.AppInfo;
import com.leyou.common.auth.pojo.Payload;
import com.leyou.common.auth.pojo.UserInfo;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.user.pojo.User;
import net.bytebuddy.asm.Advice;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 *
 */
@Service
public class AuthService {

    @Autowired
    private UserClient userClient;

    @Autowired
    private JwtProperties jwtProps;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ApplicationInfoMapper applicationInfoMapper;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    public void login(String username, String password, HttpServletResponse response) {

        //1）判断用户名和密码是否正确
        User loginUser = null;
        try {
            loginUser = userClient.query(username, password);
        }catch (Exception e){
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
        //2）如果正确，使用JwtUtils+私钥生成token
        //2.1 UserInfo存放登录用户信息
        UserInfo userInfo = new UserInfo(loginUser.getId(),loginUser.getUsername(),"admin");

        //3）生成token
        createToken(response, userInfo);

    }

    /**
     * 生成token
     * @param response
     * @param userInfo
     */
    private void createToken(HttpServletResponse response, UserInfo userInfo) {
        //2.2 生成token字符串
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo,jwtProps.getPrivateKey(),jwtProps.getCookie().getExpire());

        //3）将token写入cookie，返回给客户端
        CookieUtils.newCookieBuilder()
                .name(jwtProps.getCookie().getCookieName())
                .value(token)
                .domain(jwtProps.getCookie().getCookieDomain())
                .httpOnly(true) //禁止页面使用js获取cookie
                .response(response)
                .build();
    }

    public UserInfo verify(HttpServletRequest request,HttpServletResponse response) {
        //1.从request中取出cookie数据（获取token）
        String token = CookieUtils.getCookieValue(request, jwtProps.getCookie().getCookieName());

        //2.使用公钥去解密token
        Payload<UserInfo> payload = null;
        try {
            payload = JwtUtils.getInfoFromToken(token, jwtProps.getPublicKey(), UserInfo.class);
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }

        //判断该token是否在黑名单中
        if(redisTemplate.hasKey(payload.getId())){
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }


        //判断是否到达刷新时间点，如果到达，则刷新token的过期时间
        //1）从token中获取过期时间
        Date expireTime = payload.getExpiration();

        //2）计算刷新时间 = 过期时间-15分钟
        DateTime refreshTime = new DateTime(expireTime).minusMinutes(jwtProps.getCookie().getRefreshTime());
        //3）判断刷新时间<当前时间，刷新token的过期时间
        if(refreshTime.isBeforeNow()){
            createToken(response, payload.getInfo());
        }

        //3.从解密后tokken取出登录用信息
        return payload.getInfo();
    }

    public void logout(HttpServletRequest request, HttpServletResponse response) {
        //1）删除浏览器的Cookie  （setMaxAge(0)）
        CookieUtils.deleteCookie(
                jwtProps.getCookie().getCookieName(),
               jwtProps.getCookie().getCookieDomain(),
                response);

        //2）把当前token的id存入redis（设置黑名单），有效期设置为当前token的剩余时间
        //获取当前token的id
        String token = CookieUtils.getCookieValue(request, jwtProps.getCookie().getCookieName());
        Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, jwtProps.getPublicKey(), UserInfo.class);
        String tokenId = payload.getId();

        //获取过期时间
        Date expireTime = payload.getExpiration();
        //计算剩余的过期时间
        long remainTime = expireTime.getTime() - System.currentTimeMillis();

        redisTemplate.opsForValue().set(tokenId,"1",remainTime, TimeUnit.MILLISECONDS);
    }

    /**
     * 提供判断服务名称和服务密码是否正确
     */
    public ApplicationInfo checkServiceNameAndPwd(String serviceName,String serviceScret){
        //1.判断服务名称是否存在
        ApplicationInfo info = new ApplicationInfo();
        info.setServiceName(serviceName);
        QueryWrapper<ApplicationInfo> queryWrapper = Wrappers.query(info);
        ApplicationInfo loginAppInfo = applicationInfoMapper.selectOne(queryWrapper);

        if(loginAppInfo==null){
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }

        //2.判断密码是否正确
        if(!passwordEncoder.matches(serviceScret,loginAppInfo.getSecret())){
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }
        return loginAppInfo;
    }

    public String authorization(String serviceName, String secret) {
        //1.判断服务名和密码是否正确
        ApplicationInfo loginAppInfo = this.checkServiceNameAndPwd(serviceName,secret);

        //2.创建AppInfo，封装数据
        AppInfo appInfo = new AppInfo();
        appInfo.setId(loginAppInfo.getId());
        appInfo.setServiceName(loginAppInfo.getServiceName());
        //查询数据库得到目录服务列表
        List<String> targetList =  applicationInfoMapper.findTargetList(serviceName);
        appInfo.setTargetList(targetList);

        //3.把AppInfo存入载荷，生成token
        String token = JwtUtils.generateTokenExpireInMinutes(appInfo,jwtProps.getPrivateKey(),jwtProps.getApp().getExpire());

        //4.返回token
        return token;
    }
}
